﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace SimpleEntry
{
	/*
		Converter to handle many types.
	*/
	public class UniversalConverter : IConverter
	{
		public UniversalConverter(Type t)
		{
			this.m_DataType = t;
			SetConverter();
		}

		// careful with this in case the object is null or DBNull.
		public UniversalConverter(object o)
		{
			this.m_DataType = o.GetType();
			SetConverter();
		}

		// Special purpose constructor for DataRow use
		public UniversalConverter(DataRow dr, int cellNo)
		{
			// obtain the data type from the column
			// later perhaps from metadata
			DataColumn dc = dr.Table.Columns[cellNo];
			this.m_DataType = dc.DataType;
			SetConverter();
		}
		
		public IConverter Converter { get; set; }

		////////////////////////////////////////////////////////
		// Interface. 
		// Delegate either to installed converter or to internal overridable methods
		// 
		public String Format(object o)
		{
			if (Converter == null) return internalFormat(o);
			return Converter.Format(o);
		}

		public object Parse(String value, out String parseErrorMessage)
		{
			if (Converter == null) return internalParse(value, out parseErrorMessage);
			return Converter.Parse(value, out parseErrorMessage);
		}
/*
		public bool Validate(object o, out String errorMessage)
		{
			if (Converter == null) return internalValidate(o, out errorMessage);
			return Converter.Validate(o, out errorMessage);
		}
*/
		////////////////////////////////////////////////////////
		protected virtual String internalFormat(object o)
		{
			return o.ToString();
		}

		protected virtual object internalParse(String s, out String msg)
		{
			object o;
			ParseValidate(s, out o, out msg);
			return o;
		}
/*
		protected virtual bool internalValidate(object o, out String message)
		{
			message = "";
			return true;
		}
*/
		protected bool ParseValidate(String value, out object o, out String message)
		{
			o = null;
			if (this.m_DataType == typeof(DateTime))
			{
				DateTime dt;
				bool parseOk = (DateTime.TryParse(value, out dt));
				message = (parseOk) ? "" : DATE_PARSE_ERROR;
				o = dt;
				return parseOk;
			}
			if (this.m_DataType == typeof(int))
			{
				int iVal;
				bool parseOk = (int.TryParse(value, out iVal));
				message = (parseOk) ? "" : INT_PARSE_ERROR;
				o = iVal;
				return parseOk;
			}
			if (this.m_DataType == typeof(double)
					|| m_DataType == typeof(float)
					|| m_DataType == typeof(System.Decimal))
			{
				double dVal;
				bool parseOk = (double.TryParse(value, out dVal));
				message = (parseOk) ? "" : DOUBLE_PARSE_ERROR;
				o = dVal;
				return parseOk;
			}
			message = "";
			if (this.m_DataType == typeof(string))
			{
				o = value;
				return true;
			}
			message = String.Format("Converter cannot handle type {0} yet", m_DataType.ToString());
			return false;
		}

		protected virtual String GetParseError()
		{
			if (this.m_DataType == typeof(double)
					|| m_DataType == typeof(float)
					|| m_DataType == typeof(System.Decimal))
				return DOUBLE_PARSE_ERROR;
			if (this.m_DataType == typeof(DateTime))
				return DATE_PARSE_ERROR;
			if (this.m_DataType == typeof(int))
				return INT_PARSE_ERROR;
			if (this.m_DataType == typeof(String))
				return "Error in input format"; // not used by the base class
			// waiting for something
			return String.Format("UniversalConverter cannot handle type {0} yet", m_DataType.ToString());
		}

		private void SetConverter()
		{
			DateConverter.flags dflag = DateConverter.flags.Vanilla;
			if (this.m_DataType == typeof(int)) Converter = new IntegerConverter();
			if (this.m_DataType == typeof(DateTime)) Converter = new DateConverter(dflag);
		}

		Type m_DataType;

		const String INT_PARSE_ERROR = @"
Incorrect format for a whole number. 
Expected input is digits from 0-9. 
";
		const String DOUBLE_PARSE_ERROR = @"
Incorrect format for a number. 
Expected input is digits from 0-9 or a nuber with a decimal point. 
";
		const String DATE_PARSE_ERROR = @"
DATE_PARSE_ERROR try dd/mm/yyyy or mm/dd/yyyy
";

	}
}
